﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Reflection;
using Thallo.Configuration;

namespace Thallo {
    static class TypeInfoCache {
        static Dictionary<RuntimeTypeHandle, TypeDescriptor> _typeInfos = new Dictionary<RuntimeTypeHandle, TypeDescriptor>();
        static ReaderWriterLock _rw = new ReaderWriterLock();

        internal static Dictionary<RuntimeTypeHandle, TypeDescriptor> TypeInfos { get { return _typeInfos; } }

        public static MethodDescriptor GetMethod(MethodBase mi) {
            try {
                _rw.AcquireReaderLock(100);
                try {
                    TypeDescriptor ti;
                    if (_typeInfos.TryGetValue(mi.DeclaringType.TypeHandle, out ti))
                        return ti.Methods.Where(c => c.Method == mi).FirstOrDefault();
                    var li = _rw.UpgradeToWriterLock(100);
                    try {
                        var info = Interceptor.Current.Builder.Create(mi.DeclaringType, null);
                        _typeInfos.Add(mi.DeclaringType.TypeHandle, info);
                        return info.Methods.Where(c => c.Method == mi).FirstOrDefault();
                    } finally {
                        _rw.DowngradeFromWriterLock(ref li);
                    }

                } finally {
                    _rw.ReleaseLock();
                }
            } catch {
                return null;
            }
        }
        static Dictionary<RuntimeTypeHandle, Dictionary<string, DependencyNode>> _dependency = new Dictionary<RuntimeTypeHandle, Dictionary<string, DependencyNode>>();

        public static void BuildUpDependency() {
            var q = TypeInfos.Values.SelectMany(c => c.Methods.Select(m=>m)).Where(c=>c.DependencyAttributes != null);
            
            foreach (var item in q) {
                foreach (var d in item.DependencyAttributes) {
                    Dictionary<string, DependencyNode> dict = null;
                    if (!_dependency.TryGetValue(d.Type.TypeHandle, out dict)) {
                        dict = new Dictionary<string, DependencyNode>();
                        _dependency.Add(d.Type.TypeHandle, dict);
                    }
                    DependencyNode node;
                    if (!dict.TryGetValue(d.MethodName, out node)) {
                        node = new DependencyNode();
                        dict.Add(d.MethodName, node);
                        node.MethodName = d.MethodName;
                        node.TypeHandle = d.Type.TypeHandle;
                    }

                    node.DependencyMethods.Add(item.Method);
                }                
            }
        }

        public static DependencyNode GetDependencyNode(MethodBase mb) {
            DependencyNode node =null;
            Dictionary<string, DependencyNode> dict = null;
            if (_dependency.TryGetValue(mb.DeclaringType.TypeHandle, out dict)) {
                dict.TryGetValue(mb.Name, out node);
            }
            return node;
        }
        
    }
}
